#include "stdafx.h"
#include "RenderEMCubeMap.h"
#include "../Utility/D3D/D3DUtilityFunction.h"

float g_fExpoParam = 1.0f;
float g_fGammaParam = 1.0f;

//--------------------------------------------------------------------------------------
void WINAPI SHCubeFill( D3DXVECTOR4* pOut,
	CONST D3DXVECTOR3* pTexCoord,
	CONST D3DXVECTOR3* pTexelSize,
	LPVOID pData )
{
	SHCubeProj* pCP = ( SHCubeProj* ) pData;
	D3DXVECTOR3 vDir;

	D3DXVec3Normalize( &vDir,pTexCoord );

	float fVals[36];
	D3DXSHEvalDirection( fVals, pCP->iOrderUse, &vDir );

	( *pOut ) = D3DXVECTOR4( 0,0,0,0 ); // just clear it out...

	int l, m, uIndex = 0;
	for( l=0; l<pCP->iOrderUse; l++ )
	{
		const float fConvUse = pCP->fConvCoeffs[l];
		for( m=0; m<2*l+1; m++ )
		{
			pOut->x += fConvUse*fVals[uIndex]*pCP->pRed[uIndex];
			pOut->y += fConvUse*fVals[uIndex]*pCP->pGreen[uIndex];
			pOut->z += fConvUse*fVals[uIndex]*pCP->pBlue[uIndex];
			pOut->w = 1;

			uIndex++;
		}
	}
}

CRenderEMCubeMap::CRenderEMCubeMap():RenderTech()
{
	m_pEnvironmentMap = NULL;
	m_pEnvironmentRV = NULL;
	m_pVertexLayout = NULL;
	m_pVB = NULL;
	m_pRenderTech = NULL;
	m_pmInvWorldViewProj = NULL;
	m_pCubeMapSR = NULL;
	m_pfExpo = NULL;
	m_pfGamma = NULL;	
	m_iSHOrder = 6;
}


CRenderEMCubeMap::~CRenderEMCubeMap(void)
{
}

void CRenderEMCubeMap::InitEffectsVariables()
{
	if (!m_pEffect)
	{
#ifdef _DEBUG
		OUTPUTERRORINFO;
#endif
			return;
	}
	m_pRenderTech = m_pEffect->GetTechniqueByName("RenderSkybox");
	m_pmInvWorldViewProj = m_pEffect->GetVariableByName("mInvWorldViewProj")->AsMatrix();
	m_pCubeMapSR = m_pEffect->GetVariableByName("EMTex")->AsShaderResource();
	m_pfExpo = m_pEffect->GetVariableByName("fExpo")->AsScalar();
	m_pfGamma = m_pEffect->GetVariableByName("fGamma")->AsScalar();
}

HRESULT CRenderEMCubeMap::ComputeSHCoeffsEM(CDirectoryUtil* pDirUtil)
{
	HRESULT hr = S_OK;
	LPDIRECT3DDEVICE9 pDev9 = NULL;
	V_RETURN(CD3DUtilityFunction::CreateD3D9Device(&pDev9));

	TCHAR EMFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::EMLIGHTDIR;
	if(m_EMFileName.size()==0)
		return E_FAIL;
	pDirUtil->GetAbsoluteFileFolder(m_EMFileName.data(),subfolder, EMFileDir);

	LPDIRECT3DCUBETEXTURE9 pEM9 = NULL;
	D3D11_TEXTURE2D_DESC desc;
	m_pEnvironmentMap->GetDesc(&desc);
	V_RETURN( D3DXCreateCubeTextureFromFileEx( pDev9, EMFileDir, desc.Width, 1, 0, D3DFMT_A16B16G16R16F,
		D3DPOOL_MANAGED, D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, 0, NULL,
		NULL, &pEM9) );

	// prefilter the lighting environment by projecting onto the order 6 SH basis.  
	V(D3DXSHProjectCubeMap( m_iSHOrder, pEM9, m_fSHData[0], m_fSHData[1], m_fSHData[2] ));
	SAFE_RELEASE(pEM9);
	SAFE_RELEASE(pDev9);
	return hr;
}

HRESULT CRenderEMCubeMap::ChangeEMFile(TCHAR* strEMFileName, ID3D11Device* pd3dDevice, CDirectoryUtil* pDirUtil)
{

	m_EMFileName.clear();
	m_EMFileName.assign(strEMFileName);

	HRESULT hr = S_OK;
	//recreate the EM resources
	TCHAR EMFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::EMLIGHTDIR;
	if(m_EMFileName.size()==0)
		return E_FAIL;
	pDirUtil->GetAbsoluteFileFolder(m_EMFileName.data(),subfolder, EMFileDir);

	SAFE_RELEASE(m_pEnvironmentMap);
	SAFE_RELEASE(m_pEnvironmentRV);
	UINT SupportCaps = 0;

	pd3dDevice->CheckFormatSupport( DXGI_FORMAT_R32G32B32A32_FLOAT, &SupportCaps );
	if( SupportCaps & D3D11_FORMAT_SUPPORT_TEXTURECUBE &&
		SupportCaps & D3D11_FORMAT_SUPPORT_RENDER_TARGET &&
		SupportCaps & D3D11_FORMAT_SUPPORT_TEXTURE2D )
	{
		D3DX11_IMAGE_LOAD_INFO LoadInfo;
		LoadInfo.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
		V_RETURN( D3DX11CreateShaderResourceViewFromFile( pd3dDevice, EMFileDir, &LoadInfo, NULL, &m_pEnvironmentRV, NULL ) );
		//DXUT_SetDebugName( pCubeRV, "uffizi_cross.dds" );
		m_pEnvironmentRV->GetResource( ( ID3D11Resource** )&m_pEnvironmentMap );
		//DXUT_SetDebugName( pCubeTexture, "uffizi_cross.dds" );		
	}
	else
		return E_FAIL;

	//compute the SH projection coefficients for current lighting
	ComputeSHCoeffsEM(pDirUtil);

	return hr;
}

void CRenderEMCubeMap::ReloadEffectsDebug(ID3D11Device* pd3dDevice, CDirectoryUtil* pDirUtil)
{
	SAFE_RELEASE(m_pEffect);
	
	HRESULT hr = S_OK;
	TCHAR ShaderFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderEMCubemap.fx",subfolder, ShaderFileDir);
	V(CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect));	
	InitEffectsVariables();
}

HRESULT  CRenderEMCubeMap::OnD3D11CreateDevice(ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, 
	CDirectoryUtil* pDirUtil, void* pUserContext)
{
	HRESULT hr;
	
	TCHAR EMFileDir[MAX_PATH];
	enum SUBDIRECTORY subfolder = SUBDIRECTORY::EMLIGHTDIR;
	if(m_EMFileName.size()==0)
		return E_FAIL;
	pDirUtil->GetAbsoluteFileFolder(m_EMFileName.data(),subfolder, EMFileDir);

	SAFE_RELEASE(m_pEnvironmentMap);
	SAFE_RELEASE(m_pEnvironmentRV);
	UINT SupportCaps = 0;

	pd3dDevice->CheckFormatSupport( DXGI_FORMAT_R32G32B32A32_FLOAT, &SupportCaps );
	if( SupportCaps & D3D11_FORMAT_SUPPORT_TEXTURECUBE &&
		SupportCaps & D3D11_FORMAT_SUPPORT_RENDER_TARGET &&
		SupportCaps & D3D11_FORMAT_SUPPORT_TEXTURE2D )
	{
		D3DX11_IMAGE_LOAD_INFO LoadInfo;
		LoadInfo.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
		LoadInfo.MipLevels = 0;//create full mipmap chain;
		V_RETURN( D3DX11CreateShaderResourceViewFromFile( pd3dDevice, EMFileDir, &LoadInfo, NULL, &m_pEnvironmentRV, NULL ) );
		//DXUT_SetDebugName( pCubeRV, "uffizi_cross.dds" );
		m_pEnvironmentRV->GetResource( ( ID3D11Resource** )&m_pEnvironmentMap );
		//DXUT_SetDebugName( pCubeTexture, "uffizi_cross.dds" );
		printf("EM Cubemap texture created!\n");
	}
	else
		return E_FAIL;

	//compute the SH projection coefficients for current lighting
	ComputeSHCoeffsEM(pDirUtil);

	//create effects
	TCHAR ShaderFileDir[MAX_PATH];
	subfolder = SUBDIRECTORY::SHADERSDIR;
	pDirUtil->GetAbsoluteFileFolder(L"RenderEMCubemap.fx",subfolder, ShaderFileDir);
	V_RETURN(CD3DUtilityFunction::CreateEffectsFromFile(pd3dDevice, ShaderFileDir, NULL, "fx_5_0", &m_pEffect));	
	InitEffectsVariables();
	// Create an input layout
	D3D11_INPUT_ELEMENT_DESC skyVertexLayout[] =
	{
		{ "POSITION",  0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	SAFE_RELEASE(m_pVertexLayout);
	V(CD3DUtilityFunction::CreateVertexLayout(pd3dDevice,skyVertexLayout,ARRAYSIZE(skyVertexLayout),m_pRenderTech,&m_pVertexLayout));

	return hr;
}

HRESULT CRenderEMCubeMap::OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain, 
	const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, CDirectoryUtil* pDirUtil, void* pUserContext )
{
	HRESULT hr = S_OK;
	// Fill the vertex buffer
	D3DXVECTOR4* pVertex = new D3DXVECTOR4[4];
	if ( !pVertex )
		return E_FAIL;

	// Map texels to pixels 
	float fHighW = -1.0f - ( 1.0f / ( float )pBackBufferSurfaceDesc->Width );
	float fHighH = -1.0f - ( 1.0f / ( float )pBackBufferSurfaceDesc->Height );
	float fLowW = 1.0f + ( 1.0f / ( float )pBackBufferSurfaceDesc->Width );
	float fLowH = 1.0f + ( 1.0f / ( float )pBackBufferSurfaceDesc->Height );

	pVertex[0] = D3DXVECTOR4( fLowW, fLowH, 1.0f, 1.0f );
	pVertex[1] = D3DXVECTOR4( fLowW, fHighH, 1.0f, 1.0f );
	pVertex[2] = D3DXVECTOR4( fHighW, fLowH, 1.0f, 1.0f );
	pVertex[3] = D3DXVECTOR4( fHighW, fHighH, 1.0f, 1.0f );

	UINT uiVertBufSize = 4 * sizeof( D3DXVECTOR4 );
	//Vertex Buffer
	D3D11_BUFFER_DESC vbdesc;
	vbdesc.ByteWidth = uiVertBufSize;
	vbdesc.Usage = D3D11_USAGE_IMMUTABLE;
	vbdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vbdesc.CPUAccessFlags = 0;
	vbdesc.MiscFlags = 0;    

	D3D11_SUBRESOURCE_DATA InitData;
	InitData.pSysMem = pVertex;    
	V( pd3dDevice->CreateBuffer( &vbdesc, &InitData, &m_pVB ) );
	//DXUT_SetDebugName( m_pVB11, "SkyBox" );
	SAFE_DELETE_ARRAY( pVertex ); 

	return hr;
}

void CRenderEMCubeMap::OnD3D11ReleasingSwapChain( void* pUserContext )
{
	SAFE_RELEASE( m_pVB);
}

void CRenderEMCubeMap::OnD3D11DestroyDevice( void* pUserContext )
{
	SAFE_RELEASE(m_pEnvironmentMap);
	SAFE_RELEASE(m_pEnvironmentRV);
	SAFE_RELEASE(m_pVB);
	SAFE_RELEASE(m_pVertexLayout);
	RenderTech::OnD3D11DestroyDevice(pUserContext);
}

void CRenderEMCubeMap::OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, 
											CBaseCamera* pViewCamera, CDirectoryUtil* pDirUtil, double fTime, float fElapsedTime, 
											void* pUserContext )
{
	HRESULT hr = S_OK;
	CModelViewerCamera* pCurCamera = static_cast<CModelViewerCamera*>(pViewCamera);
	D3DXMATRIX mWorldViewProj;
	D3DXMATRIX mWorld = *pCurCamera->GetWorldMatrix();
	D3DXMATRIX mView = *pCurCamera->GetViewMatrix();
	D3DXMATRIX mProj = *pCurCamera->GetProjMatrix();
	mWorldViewProj = mWorld*mView*mProj;
	D3DXMATRIX  mInvWorldViewProj;
	D3DXMatrixInverse(&mInvWorldViewProj,NULL,&mWorldViewProj);

	// Set render resources
	pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );
	ID3D11Buffer* pVB[1] = { m_pVB };
	UINT pStride[1] = { sizeof(D3DXVECTOR4) };
	UINT pOffset[1] = { 0 };
	pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, pStride, pOffset );
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	//set texture
	hr = m_pCubeMapSR->SetResource(m_pEnvironmentRV);
	hr = m_pmInvWorldViewProj->SetMatrix((float*)&mInvWorldViewProj);
	hr = m_pfExpo->SetFloat(g_fExpoParam);
	hr = m_pfGamma->SetFloat(g_fGammaParam);

	// Render objects here...
	// Apply the technique contained in the effect 
	D3DX11_TECHNIQUE_DESC Desc;
	m_pRenderTech->GetDesc( &Desc );

	for( UINT iPass = 0; iPass < Desc.Passes; iPass++ )
	{
		m_pRenderTech->GetPassByIndex( iPass )->Apply( 0, pd3dImmediateContext );
		pd3dImmediateContext->Draw(4,0);		
	}
}